<template>
  <div class="chat-input">
    <div class="input-container">
      <textarea
        ref="inputRef"
        v-model="inputMessage"
        :placeholder="computedPlaceholder"
        :disabled="effectiveDisabled"
        class="input-textarea"
        rows="1"
        @keydown="handleKeyDown"
        @input="adjustHeight"
      />
      <button
        :disabled="effectiveDisabled"
        @click="isRecording ? stopRecorder() : wsInit()"
        :class="['send-button', { 'recording': isRecording }]"
        :title="isRecording ? '录音中，点击停止' : '开始语音'"
      >
        <template v-if="isRecording">🛑</template>
        <template v-else>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 14a3 3 0 003-3V7a3 3 0 10-6 0v4a3 3 0 003 3zm5-3a5 5 0 11-10 0H5a7 7 0 0014 0h-2zM11 19h2v3h-2v-3z" fill="currentColor"/>
          </svg>
        </template>
      </button>
      <button
        :disabled="effectiveDisabled || !inputMessage.trim()"
        @click="sendMessage"
        class="send-button"
        title="发送"
      >
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M2 21l21-9L2 3v7l15 2-15 2v7z" fill="currentColor"/>
        </svg>
      </button>
    </div>
  </div>
</template>

<script>
// 简易PCM录音器，采样率16k，提供 start/stop 与帧回调接口
class PCMRecorder {
  constructor(frameSize = 1280) {
    this.frameSize = frameSize;
    this.audioContext = null;
    this.scriptNode = null;
    this.source = null;
    this.buffer = [];
    this.onStart = null;
    this.onStop = null;
    this.onFrameRecorded = null; // ({ isLastFrame, frameBuffer:ArrayBuffer })
    this.isRecording = false;
  }

  async start() {
    if (this.isRecording) return;
    this.isRecording = true;
    const stream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: 1, sampleRate: 16000, noiseSuppression: true, echoCancellation: true } });
    this.audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 16000 });
    this.source = this.audioContext.createMediaStreamSource(stream);
    const bufferSize = 4096;
    this.scriptNode = this.audioContext.createScriptProcessor(bufferSize, 1, 1);
    this.source.connect(this.scriptNode);
    this.scriptNode.connect(this.audioContext.destination);
    if (this.onStart) this.onStart();

    this.scriptNode.onaudioprocess = (e) => {
      if (!this.isRecording) return;
      const input = e.inputBuffer.getChannelData(0);
      // Float32 -> Int16 PCM
      const pcm16 = new Int16Array(input.length);
      for (let i = 0; i < input.length; i++) {
        let s = Math.max(-1, Math.min(1, input[i]));
        pcm16[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
      }
      this.buffer.push(pcm16);

      // 按帧大小切片发送
      let total = 0;
      for (const chunk of this.buffer) total += chunk.length;
      if (total >= this.frameSize) {
        const out = new Int16Array(this.frameSize);
        let offset = 0;
        while (offset < this.frameSize && this.buffer.length) {
          const first = this.buffer[0];
          const needed = this.frameSize - offset;
          if (first.length <= needed) {
            out.set(first, offset);
            offset += first.length;
            this.buffer.shift();
          } else {
            out.set(first.subarray(0, needed), offset);
            this.buffer[0] = first.subarray(needed);
            offset += needed;
          }
        }
        const frameBuffer = out.buffer;
        if (this.onFrameRecorded) this.onFrameRecorded({ isLastFrame: false, frameBuffer });
      }
    };
  }

  stop() {
    if (!this.isRecording) return;
    this.isRecording = false;
    if (this.onStop) this.onStop();
    // 发送剩余缓冲作为最后一帧空数据通知由外层完成
    try {
      this.scriptNode && this.scriptNode.disconnect();
      this.source && this.source.disconnect();
      this.audioContext && this.audioContext.close();
    } catch (e) { /* noop */ }
    this.scriptNode = null;
    this.source = null;
    this.audioContext = null;
    this.buffer = [];
  }
}

function toBase64(buffer) {
  var binary = "";
  var bytes = new Uint8Array(buffer);
  var len = bytes.byteLength;
  for (var i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return window.btoa(binary);
}

async function hmacSha256Base64(key, msg) {
  const enc = new TextEncoder();
  const cryptoKey = await window.crypto.subtle.importKey(
    'raw',
    enc.encode(key),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );
  const signature = await window.crypto.subtle.sign('HMAC', cryptoKey, enc.encode(msg));
  // base64
  let bytes = new Uint8Array(signature);
  let binary = '';
  for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
  return btoa(binary);
}

export default {
  name: 'ChatInput',
  props: {
    disabled: {
      type: Boolean,
      default: false
    },
    // 轮次上限，由父级根据题目数量与类型计算后传入
    maxRounds: {
      type: Number,
      default: Infinity
    },
    // 当前已进行的轮次（以“一问一答”为一轮），父级维护并传入
    currentRound: {
      type: Number,
      default: 0
    },
    placeholder: {
      type: String,
      default: '请点击右侧麦克风开始说话…'
    }
  },
  data() {
    return {
      // 讯飞配置
      URL: 'wss://iat-api.xfyun.cn/v2/iat',
      APPID: '77d4c547',
      APISecret: 'MDNmNDA1ZDFlYWNlNDFjMjE2YTBlOTUy',
      APIKey: '3f33c4f210917f0e72c1ed77a8807d83',
      // 文本输入（用于展示识别结果并允许编辑）
      inputMessage: '',
      resultTextTemp: '',
      resultText: '',
      isRecording: false,
      ws: null,
      wsOpen: false,
      recorder: null
    }
  },
  computed: {
    computedPlaceholder() {
      if (this.maxRounds === Infinity) return this.placeholder;
      const remaining = Math.max(0, this.maxRounds - this.currentRound);
      return remaining > 0 ? `${this.placeholder}（剩余${remaining}轮）` : '已达本次轮次上限';
    },
    effectiveDisabled() {
      return this.disabled || (this.currentRound >= this.maxRounds);
    }
  },
  watch: {
    currentRound(newVal) {
      if (this.maxRounds !== Infinity && newVal >= this.maxRounds) {
        if (this.isRecording) {
          this.stopRecorder();
          this.closeWs();
        }
      }
    }
  },
  methods: {
    sendMessage() {
      if (this.disabled) return;
      if (this.currentRound >= this.maxRounds) {
        this.$emit('exceed-rounds');
        return;
      }
      if (this.inputMessage.trim()) {
        this.$emit('send-message', this.inputMessage.trim())
        this.inputMessage = ''
        this.adjustHeight()
      }
    },
    handleKeyDown(event) {
      if (event.key === 'Enter' && !event.shiftKey) {
        event.preventDefault()
        this.sendMessage()
      }
    },
    async wsInit() {
      if (this.disabled || this.isRecording) return;
      if (this.currentRound >= this.maxRounds) {
        this.$emit('exceed-rounds');
        return;
      }
      this.resultText = '';
      this.resultTextTemp = '';
      // 清空当前输入框，准备放置识别结果
      this.inputMessage = '';
      const url = await this.getWebSocketUrl();
      this.ws = new WebSocket(url);
      this.ws.onopen = () => {
        this.wsOpen = true;
        const firstFrame = {
          common: { app_id: this.APPID },
          business: { language: 'zh_cn', domain: 'iat', accent: 'mandarin', vad_eos: 6000, dwa: 'wpgs' },
          data: { status: 0, format: 'audio/L16;rate=16000', encoding: 'raw' }
        };
        this.ws.send(JSON.stringify(firstFrame));
        // 启动录音
        this.recorder = new PCMRecorder(1280);
        this.recorder.onStart = () => { this.isRecording = true; };
        this.recorder.onStop = () => { this.isRecording = false; };
        this.recorder.onFrameRecorded = ({ isLastFrame, frameBuffer }) => {
          if (!this.wsOpen) return;
          const params = {
            data: {
              status: 1,
              format: 'audio/L16;rate=16000',
              encoding: 'raw',
              audio: toBase64(frameBuffer)
            }
          };
          this.ws.send(JSON.stringify(params));
        };
        this.recorder.start();
      };
      this.ws.onmessage = (message) => {
        const jsonData = JSON.parse(message.data);
        if (jsonData.data && jsonData.data.result) {
          const data = jsonData.data.result;
          let str = '';
          let ws = data.ws;
          for (let i = 0; i < ws.length; i++) {
            str = str + ws[i].cw[0].w;
          }
          if (data.pgs) {
            if (data.pgs === 'apd') {
              this.resultText = this.resultTextTemp;
            }
            this.resultTextTemp = this.resultText + str;
          } else {
            this.resultText = this.resultText + str;
          }
          // 录音过程中不更新到输入框，结束后一次性写入
        }
        if (jsonData.code === 0 && jsonData.data && jsonData.data.status === 2) {
          this.stopRecorder();
          this.closeWs();
          const finalText = (this.resultTextTemp || this.resultText || '').trim();
          // 将最终识别文本放入输入框，供用户编辑与发送
          if (finalText) this.inputMessage = finalText;
          this.adjustHeight();
        }
        if (jsonData.code !== 0) {
          this.closeWs();
          // 可根据需要提示错误
        }
      };
      this.ws.onclose = () => { this.wsOpen = false; };
      this.ws.onerror = () => { this.wsOpen = false; };
    },
    stopRecorder() {
      if (this.recorder) this.recorder.stop();
      if (this.wsOpen && this.ws) {
        const end = { data: { status: 2, format: 'audio/L16;rate=16000', encoding: 'raw', audio: '' } };
        try { this.ws.send(JSON.stringify(end)); } catch (e) {}
      }
    },
    closeWs() {
      if (this.ws) {
        try { this.ws.close(); } catch (e) {}
        this.ws = null;
      }
      this.wsOpen = false;
      this.isRecording = false;
    },
    async getWebSocketUrl() {
      const url = this.URL;
      const host = 'iat-api.xfyun.cn';
      const date = new Date().toGMTString();
      const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`;
      const signature = await hmacSha256Base64(this.APISecret, signatureOrigin);
      const authorizationOrigin = `api_key="${this.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`;
      const authorization = btoa(unescape(encodeURIComponent(authorizationOrigin)));
      return `${url}?authorization=${authorization}&date=${encodeURI(date)}&host=${host}`;
    },
    adjustHeight() {
      this.$nextTick(() => {
        const textarea = this.$refs.inputRef;
        if (!textarea) return;
        textarea.style.height = 'auto';
        textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
      })
    },
    focus() {
      this.$refs.inputRef && this.$refs.inputRef.focus()
    }
  },
  mounted() {
    this.adjustHeight()
  }
}
</script>

<style scoped>
.chat-input {
  padding: 20px;
  background: rgba(0, 35, 120, 0.3);
  border-top: 1px solid rgba(100, 162, 255, 0.3);
  backdrop-filter: blur(10px);
}

.input-container {
  position: relative;
  display: flex;
  align-items: center;
  max-width: 800px;
  margin: 0 auto;
  padding: 8px 12px; /* 左右布局：不再为按钮预留叠加空间 */
  border: 1px solid rgba(100, 162, 255, 0.4);
  border-radius: 24px;
  background: rgba(0, 35, 120, 0.6);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.06);
}

.input-textarea {
  flex: 1;
  width: 100%;
  padding: 8px 8px;
  border: none; /* 去掉自身边框，避免竖线 */
  border-radius: 16px;
  font-size: 16px; /* 提高可读性 */
  line-height: 1.5;
  resize: none;
  outline: none;
  transition: all 0.2s ease;
  min-height: 44px;
  max-height: 120px;
  overflow-y: auto;
  background: transparent; /* 由容器绘制背景与边框 */
  color: #fff;
}

.input-textarea::placeholder {
  color: rgba(255, 255, 255, 0.6);
}

.input-textarea:focus {
  box-shadow: none;
}

.input-textarea:disabled {
  background: rgba(0, 35, 120, 0.3);
  color: rgba(255, 255, 255, 0.4);
  cursor: not-allowed;
}

.send-button {
  width: 36px;
  height: 36px;
  background: linear-gradient(135deg, #0258f0, #0066ff);
  border: 1px solid rgba(100, 162, 255, 0.6);
  border-radius: 50%;
  color: white;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;
  flex-shrink: 0;
  backdrop-filter: blur(5px);
  margin-left: 12px; /* 左右布局的间距 */
}

.send-button:hover:not(:disabled) {
  background: linear-gradient(135deg, #0066ff, #0080ff);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(2, 88, 240, 0.4);
}

.send-button:disabled {
  background: rgba(100, 162, 255, 0.3);
  border-color: rgba(100, 162, 255, 0.2);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.send-button.recording {
  background: #ff4d4f;
  border-color: rgba(255, 77, 79, 0.8);
}

/* 滚动条样式 */
.input-textarea::-webkit-scrollbar {
  width: 4px;
}

.input-textarea::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
}

.input-textarea::-webkit-scrollbar-thumb {
  background: rgba(100, 162, 255, 0.5);
  border-radius: 2px;
}

.input-textarea::-webkit-scrollbar-thumb:hover {
  background: rgba(100, 162, 255, 0.7);
}

@media (max-width: 768px) {
  .chat-input {
    padding: 15px;
  }
  
  .input-container {
    gap: 8px;
  }
  
  .input-textarea {
    font-size: 16px; /* 防止在移动设备上自动缩放 */
  }
}
</style>
